home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / x11 / rpg / crossfir.001 / crossfir~ / eutl / tcplib / tcplib.c < prev    next >
C/C++ Source or Header  |  1994-11-05  |  16KB  |  638 lines

  1. #include <sys/types.h>
  2. #include <sys/time.h>
  3. #include <sys/socket.h>
  4. #include <netinet/in.h>
  5. #include <netdb.h>
  6. #include <stdlib.h>
  7. #include <errno.h>
  8. #include <stdio.h>
  9. #include "errlib.h"
  10. #include "xmalloc.h"
  11. #include "tcplib.h"
  12. #include "libc.h"
  13.  
  14. /*
  15.  *   eutl - A collection of useful libraries
  16.  *   tcplib - A set of routines making tcp stuff easier.
  17.  *
  18.  *   (c) Copyright 1993 Eric Anderson 
  19.  *
  20.  * My thanks to Geoffrey Collyer and Henry Spencer for providing the basis
  21.  * for this copyright.
  22.  *
  23.  * Permission is granted to anyone to use this software for any purpose on
  24.  * any computer system, and to alter it and redistribute it freely, subject
  25.  * to the following restrictions:
  26.  *
  27.  * 1. The authors are not responsible for the consequences of use of this
  28.  *    software, no matter how awful, even if they arise from flaws in it.
  29.  *
  30.  * 2. The origin of this software must not be misrepresented, either by
  31.  *    explicit claim or by omission.  Since few users ever read sources,
  32.  *    credits must appear in the documentation.
  33.  *
  34.  * 3. Altered versions must be plainly marked as such, and must not be
  35.  *    misrepresented as being the original software.  Since few users
  36.  *    ever read sources, credits must appear in the documentation.
  37.  *
  38.  * 4. This notice may not be removed or altered.
  39.  */
  40.  
  41. /* 
  42.    System Configuration Options.
  43.    Descriptions of each configuration option contained in the default
  44.    section at the end.
  45. */
  46.  
  47. #ifdef sun
  48. #define STATIC_XADDR 1
  49. #endif
  50.  
  51. #ifdef sgi
  52. #define BUFFER_READ 0
  53. #define USE_IOVEC 0
  54. #endif
  55.  
  56. /* 
  57.    Default Section
  58. */
  59.  
  60. #ifndef STATIC_XADDR
  61. /*
  62.    On the sun, you seem to need to have struct sockaddr_in, and the
  63.    xaddrlen static when you do an accept, or you get an EFAULT.
  64.    See the comment in AcceptConnection for more information.
  65. */
  66. #define STATIC_XADDR 0
  67. #endif
  68.  
  69. #ifndef BUFFER_READ
  70. /*
  71.    This decides whether or not to buffer the information on read.
  72.    On the Sgi's the system seems to not return with the amount of data which
  73.    is on the socket immediately, and instead waits for a little while.
  74.    Hence it is a performance hit to attempt to pre-read data 
  75. */
  76. #define BUFFER_READ 1
  77. #endif
  78.  
  79. #ifndef USE_IOVEC
  80. /* 
  81.    This decides whether or not we will actually use iovec.  If the system
  82.    doesn't implement iovec more cleverly than just doing a set of reads or
  83.    writes, we are better off doing the stuff ourselves because the behavior
  84.    is more likely to be as expected.
  85. */
  86. #define USE_IOVEC HAS_IOVEC
  87. #endif
  88.  
  89. /*
  90.    END of Default Section
  91. */
  92.  
  93. #define DEFAULT_AUTOFLUSHLEN 100000
  94.  
  95. struct __TcpSocket {
  96.   int fd;
  97.   char *rcvbuf,*sendbuf,*sizedbuf;
  98.   long rcvbufsize,sendbufsize,rcvbufvalid,sizedbufsize;
  99.   char *rcvbufpos,*sendbufpos;
  100.   long autoflushlen; /* Length at which to automatically flush output.
  101.             -- Prevents massively filling the output buffer,
  102.             causing the system to allocate lots of space */
  103.   struct {
  104.     unsigned int hasinput:1;
  105.     unsigned int haserror:1;
  106.     unsigned int isforeign:1;
  107.     unsigned int isserver:1;
  108.     unsigned int bufferout:1;
  109.     unsigned int incrsize:1;
  110.     unsigned int checkwrite:1;
  111.     unsigned int readywrite:1;
  112.   } flags;
  113. };
  114.  
  115. const int RCVINIT = 8192;
  116. const int RCVINCR = 8192;
  117.  
  118. char *tcplib_packagever = "TcpLib V2.0";
  119. static struct protoent *protox;
  120. static ErrorFunction TcpLibErf = LongJmpErrorFunction;
  121.  
  122. char *tcplib_EProtoByName = "Error getting protocol by name";
  123. char *tcplib_ESocket = "Error on socket system call";
  124. char *tcplib_ESockOpt = "Error setting socket option";
  125. char *tcplib_EBindFailed = "Bind system call failed";
  126. char *tcplib_EListenFailed = "Listen system call failed";
  127. char *tcplib_ELookupFailed = "Lookup of hostname failed";
  128. char *tcplib_ECloseErr = "Error on close call";
  129. char *tcplib_EConnectFailed = "Connect call failed";
  130. char *tcplib_EInvalidArg = "Invalid Argument passed to function";
  131. char *tcplib_EAcceptBad = "Error on accept system call";
  132. char *tcplib_EWriteFailed = "Error on write system call";
  133. char *tcplib_EWriteTooMuch = "Internal Error: write() wrote too much";
  134. char *tcplib_EReadError = "Error on read system call";
  135. char *tcplib_EBadWait = "Invalid arguments to Wait";
  136. char *tcplib_ESelectError = "Error on select system call";
  137. char *tcplib_ENothingRead = "Couldn't read data, socket probably closed";
  138.  
  139. void tcplib_SetDefaultErrorFunction(ErrorFunction erf)
  140. {
  141.   TcpLibErf = erf;
  142. }
  143.  
  144. static TcpSocket initTcpSocket()
  145. {
  146.   TcpSocket ret;
  147.  
  148.   ret = xbzmalloc(sizeof(struct __TcpSocket));
  149.   ret->autoflushlen = DEFAULT_AUTOFLUSHLEN;
  150.   return ret;
  151. }
  152.  
  153. int GetTcpSocketFD(TcpSocket of)
  154. {
  155.   return of->fd;
  156. }
  157.  
  158. #ifdef SO_LINGER
  159. static struct linger lingeroff={0,0};
  160. #endif
  161.  
  162. static int GetSocket()
  163. {
  164.   int fd;
  165.  
  166.   protox = getprotobyname("tcp");
  167.  
  168.   if (protox==NULL) {
  169.     TcpLibErf(tcplib_packagever,tcplib_EProtoByName,
  170.           "Error getting protocol by name\n");
  171.   }
  172.   fd = socket(PF_INET,SOCK_STREAM,protox->p_proto);
  173.   if (fd<=0) {
  174.     TcpLibErf(tcplib_packagever,tcplib_ESocket,"Error on socket command: %s\n",
  175.           strerror(errno));
  176.   }
  177.  
  178. #ifdef SO_REUSEADDR
  179.   { 
  180.     int one = 1;
  181.     if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one))) {
  182.       TcpLibErf(tcplib_packagever,tcplib_ESockOpt,
  183.         "Error setting ReuseAddr Socket option: %s\n",strerror(errno));
  184.     }
  185.   }
  186. #endif
  187.  
  188. #ifdef SO_LINGER
  189.   if (setsockopt(fd,SOL_SOCKET,SO_LINGER,
  190.          (char *)&lingeroff,sizeof(lingeroff))) {
  191.     TcpLibErf(tcplib_packagever,tcplib_ESockOpt,
  192.           "Error setting Linger Socket option: %s\n",strerror(errno));
  193.   }
  194. #endif
  195.   return fd;
  196. }
  197.  
  198. static TcpSocket newSocket()
  199. {
  200.   TcpSocket ret;
  201.  
  202.   ret = initTcpSocket();
  203.   WITH_HANDLING {
  204.     ret->fd = GetSocket();
  205.   } HANDLE {
  206.     free(ret);
  207.     RERAISE();
  208.   }
  209.   END_HANDLING
  210.   return ret;
  211. }
  212.  
  213. TcpSocket BecomeServer(int port)
  214. {
  215.   TcpSocket ret;
  216.   struct sockaddr_in insock;
  217.  
  218.   ret = newSocket();
  219.  
  220.   ret->flags.isserver = 1;
  221.   insock.sin_family = AF_INET;
  222.   insock.sin_port = htons((unsigned short)port);
  223.   insock.sin_addr.s_addr = htonl(INADDR_ANY);
  224.   
  225.   if (bind(ret->fd,&insock,sizeof(insock))<0) {
  226.     close(ret->fd);
  227.     free(ret);
  228.  
  229.     TcpLibErf(tcplib_packagever,tcplib_EBindFailed,
  230.           "Error binding socket to port %d: %s\n",port,strerror(errno));
  231.   }
  232.  
  233.   if (listen(ret->fd,5)<0) { 
  234.     close(ret->fd);
  235.     free(ret);
  236.     TcpLibErf(tcplib_packagever,tcplib_EListenFailed,
  237.           "Error Listening for clients: %s\n",strerror(errno));
  238.   }
  239.   return ret;
  240. }
  241.  
  242. TcpSocket GetConnection(char *hostname,int port)
  243. {
  244.   TcpSocket ret;
  245.   struct hostent *remotehost;
  246.   struct sockaddr_in insock;
  247.  
  248.   if (hostname==NULL)
  249.     hostname = "localhost";
  250.  
  251.   remotehost = gethostbyname(hostname);
  252.   if (remotehost == NULL) {
  253.     TcpLibErf(tcplib_packagever,tcplib_ELookupFailed,
  254.           "Unable to find host %s: %s\n",hostname,strerror(errno));
  255.   }
  256.     
  257.   ret = newSocket();
  258.   insock.sin_family = AF_INET;
  259.   insock.sin_port = htons((unsigned short)port);
  260. /*  insock.sin_addr.s_addr = (remotehost->h_addr_list[0][0]<<0) +
  261.         (remotehost->h_addr_list[0][1]<<8)+
  262.         (remotehost->h_addr_list[0][2]<<16)+
  263.     (remotehost->h_addr_list[0][3]<<24);*/
  264.   bcopy((void *)remotehost->h_addr, 
  265.     (void *)&insock.sin_addr, remotehost->h_length);
  266.  
  267. /*
  268.   insock.sin_addr.s_addr = htonl((unsigned long)remotehost->h_addr_list[0]);
  269. */
  270.   
  271.   if (connect(ret->fd,&insock,sizeof(struct sockaddr_in))) {
  272.     TcpLibErf(tcplib_packagever,tcplib_EConnectFailed,
  273.           "Unable to connect to host %s port %d: %s\n",
  274.           hostname,port,strerror(errno));
  275.   }
  276.   return ret;
  277. }
  278.  
  279. #if STATIC_XADDR
  280. #define SSTATIC_XADDR static
  281. #else
  282. #define SSTATIC_XADDR 
  283. #endif
  284.  
  285. TcpSocket AcceptConnection(TcpSocket server,unsigned long *from)
  286. {
  287. /* I do not know why this needs to be static.  However, I have observed that
  288.    without making it static, I get a EFAULT on the accept, so therefore I
  289.    am making it static to make it work.  I'd appreciate an explanation if
  290.    anyone has one. */
  291.   SSTATIC_XADDR struct sockaddr_in xaddr;
  292.   SSTATIC_XADDR int xaddrlen;
  293.   int inputfd;
  294.   TcpSocket ret;
  295.  
  296.   if (server->flags.isserver == 0) {
  297.     TcpLibErf(tcplib_packagever,tcplib_EInvalidArg,
  298.           "Attempted to accept connection on non-server port\n");
  299.   }
  300.   if ((inputfd = accept(server->fd,&xaddr,&xaddrlen))<0) {
  301.     TcpLibErf(tcplib_packagever,tcplib_EAcceptBad,
  302.           "Accept Failed: %s\n",strerror(errno));
  303.   }
  304.   WITH_HANDLING {
  305.     ret = initTcpSocket();
  306.   } HANDLE {
  307.     close(inputfd);
  308.     RERAISE();
  309.   }
  310.   END_HANDLING;
  311.   if (from) {
  312.     *from = ntohl(xaddr.sin_addr.s_addr);
  313.   }
  314.   ret->fd = inputfd;
  315.   return ret;
  316. }
  317.  
  318. TcpSocket MakeForeignFDConnection(int fd)
  319. {
  320.   TcpSocket ret;
  321.  
  322.   ret = initTcpSocket();
  323.   ret->fd = fd;
  324.   ret->flags.isforeign = 1;
  325.   return ret;
  326. }
  327.  
  328. void CloseConnection(TcpSocket gone)
  329. {
  330.   if (gone->rcvbuf) free(gone->rcvbuf);
  331.   if (gone->sendbuf) free(gone->sendbuf);
  332.   if (gone->sizedbuf) free(gone->sizedbuf);
  333.   if (!gone->flags.isforeign) 
  334.     if(close(gone->fd)) 
  335.       TcpLibErf(tcplib_packagever,tcplib_ECloseErr,
  336.         "Error Closing Connection: %s\n",strerror(errno));
  337.   free(gone);
  338. }
  339.  
  340. static void __DoSendMsg(int fd,char *msg,unsigned long msglen)
  341. {
  342.   long amt;
  343.  
  344.   while(msglen>0) {
  345.     amt = write(fd,msg,msglen);
  346.     if (amt<0) {
  347.       TcpLibErf(tcplib_packagever,tcplib_EWriteFailed,
  348.         "Error on write in SendMessage: %s\n",strerror(errno));
  349.     }
  350.     msg += amt;
  351.     msglen -= amt;
  352.   }
  353. }
  354.  
  355. void TcpLibFlush(TcpSocket to)
  356. {
  357.   if (to->sendbufpos>to->sendbuf)
  358.     __DoSendMsg(to->fd,to->sendbuf,to->sendbufpos-to->sendbuf);
  359.   to->sendbufpos = to->sendbuf;
  360. }
  361.  
  362. void TcpLibBufferOutput(TcpSocket sock,int on)
  363. {
  364.   if (on)
  365.     sock->flags.bufferout = 1;
  366.   else {
  367.     TcpLibFlush(sock);
  368.     sock->flags.bufferout = 0;
  369.   }
  370. }
  371.     
  372. void IOVSendMsg(TcpSocket to,struct iovec *vecs,int nvecs)
  373. {
  374.   if (to->flags.bufferout) {
  375.     int l;
  376.  
  377.     for(l=0;l<nvecs;l++)
  378.       SendMsg(to,vecs[l].iov_base,vecs[l].iov_len);
  379.   } else {
  380. #if USE_IOVEC
  381.     int f;
  382.     long amt;
  383.  
  384.     f = 0;
  385.     while (1) {
  386.       amt = writev(to->fd,vecs,nvecs);
  387.       if (amt<0) {
  388.     TcpLibErf(tcplib_packagever,tcplib_EWriteFailed,
  389.           "Error on writev in IOVSendMsg: %s\n",strerror(errno));
  390.       }
  391.       while (amt>vecs[f].iov_len) {
  392.     amt -= vecs[f].iov_len;
  393.     vecs[f].iov_len = 0;
  394.     f++;
  395.     if (f==nvecs) {
  396.       TcpLibErf(tcplib_packagever,tcplib_EWriteTooMuch,
  397.             "Internal Error: writev wrote more data than we have??\n");
  398.     }
  399.       }
  400.       vecs[f].iov_len -= amt;
  401.       if (vecs[f].iov_len == 0)
  402.     break;            /* We're done writing */
  403.     }
  404. #else
  405.     int l;
  406.     for(l=0;l<nvecs;l++)
  407.       __DoSendMsg(to->fd,vecs[l].iov_base,vecs[l].iov_len);
  408. #endif
  409.   }
  410. }
  411.   
  412. void SendMsg(TcpSocket to,char *msg,unsigned long msglen)
  413. {
  414.   long used;
  415.   if (to->flags.bufferout) {
  416.     used = to->sendbufpos - to->sendbuf;
  417.     if (used+msglen > to->autoflushlen) {
  418.       TcpLibFlush(to);
  419.       __DoSendMsg(to->fd,msg,msglen);
  420.       return;
  421.     }
  422.     if ((to->sendbuf == NULL) ||
  423.     (to->sendbufsize - (used))<msglen) {
  424.       to->sendbuf = xrealloc(to->sendbuf,used + msglen);
  425.       to->sendbufpos = to->sendbuf + used;
  426.       to->sendbufsize = used+msglen;
  427.     }
  428.     bcopy(msg,to->sendbufpos,msglen);
  429.     to->sendbufpos += msglen;
  430.   } else {
  431.     __DoSendMsg(to->fd,msg,msglen);
  432.   }
  433. }
  434.  
  435. void SendSizedMsg(TcpSocket to,char *buf,long size)
  436. {
  437.   char sbuf[4];
  438.   struct iovec iov[2];
  439.  
  440.   sbuf[0] = (unsigned long)size >> 24;
  441.   sbuf[1] = ((unsigned long)size >> 16) & 0xFF;
  442.   sbuf[2] = ((unsigned long)size >> 8) & 0xFF;
  443.   sbuf[3] = ((unsigned long)size) & 0xFF;
  444.   
  445.   iov[0].iov_base = sbuf;
  446.   iov[0].iov_len = 4;
  447.   iov[1].iov_base = buf;
  448.   iov[1].iov_len = size;
  449.   IOVSendMsg(to,iov,2);
  450. }
  451.  
  452. void SendString(TcpSocket to,char *buf)
  453. {
  454.   SendSizedMsg(to,buf,strlen(buf)+1);
  455. }
  456.  
  457. #if ! BUFFER_READ
  458. static long eread(TcpSocket from, char *buf,long amt)
  459. {
  460.   long ramt;
  461.   
  462.   ramt = read(from->fd,buf,amt);
  463.   if (ramt < 0) {
  464.     TcpLibErf(tcplib_packagever,EReadError,
  465.           "Error reading from socket: %s\n",strerror(errno));
  466.   }
  467.   return ramt;
  468. }
  469. #else
  470. static long eread(TcpSocket from,char *buf,long amt)
  471. {
  472.   long ramt;
  473.   
  474.  readfrombuf:
  475.   if (from->rcvbufvalid) {
  476.     long min = from->rcvbufvalid;
  477.     if (amt<min)
  478.       min = amt;
  479.     bcopy(from->rcvbufpos,buf,min);
  480.     from->rcvbufpos += min;
  481.     from->rcvbufvalid -= min;
  482.     return min;
  483.     goto readfrombuf;
  484.   }
  485.   if (from->rcvbuf == NULL) {
  486.     from->rcvbuf = xmalloc(RCVINIT);
  487.     from->rcvbufsize = RCVINIT;
  488.   }
  489.   if (from->flags.incrsize) {
  490.     free(from->rcvbuf);
  491.     from->rcvbufsize += RCVINCR;
  492.     from->rcvbuf = xmalloc(from->rcvbufsize);
  493.     from->flags.incrsize = 0;
  494.   }
  495.   from->rcvbufpos = from->rcvbuf;
  496.   from->rcvbufvalid = 0;
  497. /* Using readv should be more efficient since it may lower by one the number
  498.    of copies we have to make of input data. */
  499. #if USE_IOVEC
  500.   {
  501.     struct iovec iov[2];
  502.  
  503.     iov[0].iov_base = buf;
  504.     iov[0].iov_len = amt;
  505.     iov[1].iov_base = from->rcvbuf;
  506.     iov[1].iov_len = from->rcvbufsize;
  507.     ramt = readv(from->fd,iov,2);
  508.     if (ramt <0) {
  509.       from->flags.haserror = 1;
  510.       TcpLibErf(tcplib_packagever,tcplib_EReadError,
  511.         "Error reading from socket: %s\n",strerror(errno));
  512.     }
  513.     if (ramt<amt)
  514.       return ramt;        /* Didn't fill our buffer at all */
  515.     ramt -= amt;
  516.     from->rcvbufvalid = ramt;
  517.     if (from->rcvbufvalid == from->rcvbufsize)
  518.       from->flags.incrsize = 1;
  519.     return amt;
  520.   }
  521. #else
  522.   ramt = read(from->fd,from->rcvbuf,from->rcvbufsize);
  523.   if (ramt <0) {
  524.     TcpLibErf(tcplib_packagever,EReadError,
  525.           "Error reading from socket: %s\n",strerror(errno));
  526.   }
  527.   from->rcvbufvalid = ramt;
  528.   if (from->rcvbufvalid == from->rcvbufsize)
  529.     from->flags.incrsize = 1;
  530.   if (ramt==0)
  531.     return 0;/* Prevent loops */
  532.   goto readfrombuf;
  533. #endif
  534. }
  535. #endif
  536.  
  537. void GetMsg(TcpSocket from,char *buf,long size)
  538. {
  539.   long t;
  540.   
  541.   if (from->sendbufpos > from->sendbuf)
  542.     TcpLibFlush(from);
  543.   while(size>0) {
  544.     t = eread(from,buf,size);
  545.     size -= t;
  546.     buf += t;
  547.     /* It seems as though we should do something if the amount we read in
  548.        is 0.  Because otherwise this code will go into a tight busy-wait
  549.        loop */
  550.     if (t==0)
  551.       TcpLibErf(tcplib_packagever,tcplib_ENothingRead,
  552.         "Tried to read data, but nothing was read, socket probably closed.\n");
  553.   }
  554. }
  555.  
  556. char *GetSizedMsg(TcpSocket from,long *size)
  557. {
  558.   unsigned char csize[4];
  559.   long x;
  560.   
  561.   GetMsg(from,(char *)csize,4);
  562.   x = (csize[0] << 24) + (csize[1] << 16) + (csize[2] << 8) + csize[3];
  563.   if (from->sizedbufsize<x) {
  564.     from->sizedbuf = xrealloc(from->sizedbuf,x);
  565.     from->sizedbufsize = x;
  566.   }
  567.   GetMsg(from,from->sizedbuf,x);
  568.   if (size)
  569.     *size = x;
  570.   return from->sizedbuf;
  571. }
  572.  
  573. char *GetString(TcpSocket from)
  574. {
  575.   return GetSizedMsg(from,NULL);
  576. }
  577.  
  578. int WaitForInput(TcpSocket *socks,int nsocks,long msTimeout)
  579. {
  580.   fd_set rmaskfd,wmaskfd,emaskfd;
  581.   int nfound,l,maxfd;
  582.   struct timeval max_wait;
  583.  
  584.   if (nsocks<=0 && msTimeout == TCPLIB_FOREVER)
  585.     TcpLibErf(tcplib_packagever,tcplib_EBadWait,
  586.           "Tried to wait with no sockets and no timeout\n");
  587.  
  588.   FD_ZERO(&rmaskfd);FD_ZERO(&wmaskfd);FD_ZERO(&emaskfd);
  589.   for(l=0;l<nsocks;l++) {
  590.     TcpLibFlush(socks[l]);
  591.     socks[l]->flags.haserror = 0;
  592.     socks[l]->flags.hasinput = socks[l]->rcvbufvalid > 0;
  593.   }
  594.   for(l=0;l<nsocks;l++) {
  595.     if (socks[l]->flags.hasinput)
  596.       return 1;
  597.   }
  598.   if (msTimeout != TCPLIB_FOREVER) {
  599.     max_wait.tv_sec = msTimeout / 1000;
  600.     max_wait.tv_usec = (msTimeout % 1000) * 1000;
  601.   }
  602.     
  603.   maxfd = 0;
  604.   for(l=0;l<nsocks;l++) {
  605.     FD_SET(socks[l]->fd,&rmaskfd);
  606.     FD_SET(socks[l]->fd,&emaskfd);
  607.     if (socks[l]->flags.checkwrite) 
  608.       FD_SET(socks[l]->fd,&wmaskfd);
  609.     if (socks[l]->fd >= maxfd)
  610.       maxfd = socks[l]->fd + 1;
  611.   }
  612.   
  613.   nfound = select(maxfd,&rmaskfd,&wmaskfd,&emaskfd,
  614.           msTimeout != TCPLIB_FOREVER ? &max_wait : NULL);
  615.   if (nfound <= 0)
  616.     return 0;
  617. #if 0
  618.   if (nfound < 0)
  619.     TcpLibErf(tcplib_packagever,tcplib_ESelectError,
  620.           "Error on Select: %s\n",strerror(errno));
  621. #endif
  622.   for(l=0;l<nsocks;l++) {
  623.     if (FD_ISSET(socks[l]->fd,&rmaskfd))
  624.       socks[l]->flags.hasinput = 1;
  625.     if (FD_ISSET(socks[l]->fd,&wmaskfd)) 
  626.       socks[l]->flags.readywrite = 1;
  627.     if (FD_ISSET(socks[l]->fd,&emaskfd))
  628.       socks[l]->flags.haserror = 1;
  629.   }
  630.   return nfound;
  631. }
  632.  
  633. int HasInput(TcpSocket sock)
  634. {
  635.   return sock->flags.hasinput;
  636. }
  637.  
  638.